03. Code Checkpoint: viewModelScope and TestCoroutineDispatcher

L5 P4 A03 ViewModelScope And TestCoroutineDispatcher V3

In this step you'll write a test for code that uses viewModelScope - you'll write a test that checks that when a task is completed, the snackbar shows the correct message. To complete this test, you'll need to swap the default Dispatchers.Main for a TestCoroutineDispatcher.

If you're curious, you can learn more about viewModelScope in the blogpost Easy Coroutines in Android: viewModelScope.

Optional Step: Download the Code (Code Checkpoint)

If you haven't been following along or want to download the code up to this point, you can do so now. Download the code here, download a zip of the code here, OR you can clone the Github repository for the code:

$ git clone https://github.com/udacity/android-testing.git
$ cd android-architecture
$ git checkout end_codelab_2

Step 1: Observe the Issue

Start by adding the new completeTask_dataAndSnackbarUpdated test.

  1. Open test > tasks > TasksViewModelTest
  2. Add this new test method:

TasksViewModelTest.kt

@Test
fun completeTask_dataAndSnackbarUpdated() {
    // Create an active task and add it to the repository.
    val task = Task("Title", "Description")
    tasksRepository.addTasks(task)

    // Mark the task as complete task.
    tasksViewModel.completeTask(task, true)

    // Verify the task is completed.
   assertThat(tasksRepository.tasksServiceData[task.id]?.isCompleted, `is`(true))

    // Assert that the snackbar has been updated with the correct text.
    val snackbarText: Event<Int> =  tasksViewModel.snackbarText.getOrAwaitValue()
    assertThat(snackbarText.getContentIfNotHandled(), `is`(R.string.task_marked_complete))
}
  1. Run this new test. You should observe that it fails with the following error:

"Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used."

Step 2: Replace Dispatcher.Main with TestCoroutineDispatcher

  1. In TasksViewModelTest, create a TestCoroutineDispatcher as a val called testDispatcher.

Swap this testDispatcher in exchange for the standard Main dispatcher.

  1. Create a @Before method that calls Dispatchers.setMain(testDispatcher) before every test.
  2. Create an @After method that cleans everything up after running each test by calling Dispatchers.resetMain() and then testDispatcher.cleanupTestCoroutines().

Here's what this code looks like:

TasksViewModelTest.kt

@ExperimentalCoroutinesApi
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()

@ExperimentalCoroutinesApi
@Before
fun setupDispatcher() {
    Dispatchers.setMain(testDispatcher)
}

@ExperimentalCoroutinesApi
@After
fun tearDownDispatcher() {
    Dispatchers.resetMain()
    testDispatcher.cleanupTestCoroutines()
}
  1. Run your test again. It should now pass! Good work!